AOP动态代理--基本原理

 AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。

今天来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。

        AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
        那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
        AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。
还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释
package com.service;
/**
 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口 
 * @author xx
 * 2017年3月24日 下午4:17:53
 */
public interface StudentInfoService {
	void findInfo(String studentName);
}
2、实现类,我们真正的业务代码,需要做切面

package com.service.impl;

import com.service.StudentInfoService;

public class StudentInfoServiceImpl implements StudentInfoService{

	@Override
	public void findInfo(String studentName) {
		//
		System.out.println("#####你目前输入的名字是:"+studentName);
		System.out.println("#####业务代码xxx1");
		System.out.println("#####业务代码xxx2");
	}

}
3,实现动态动态代理类
  3.1先写一个工厂类AOPFactory ,连接需要代理的接口(可以不写)
package com.handler;

public class AOPFactory {	
	public static <T> Object getAOPProxyedObject(Class<T> cls){
		Object proxy = null;
		MyHandler handler = new MyHandler();
		try {
			Object obj = cls.newInstance();
			if (obj != null) {
				proxy = handler.bind(obj);
			} else {
				System.out.println("Can't get the proxyobj");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	
		return proxy;
	}
}

 代理类MyHandler
package com.handler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.log4j.Logger;

public class MyHandler implements InvocationHandler {
	private Object proxyObj;
	private static Logger log = Logger.getLogger(MyHandler.class);

	public Object bind(Object obj) {
		this.proxyObj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);
	}
	@Override
	public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
		Object result=null;
		try{
		//请在这里插入代码,在方法前调用
		log.debug("调用log日志方法 before"+method.getName());
		result=method.invoke(proxyObj,args); //原方法
		//请在这里插入代码,方法后调用
		log.debug("调用log日志方法 after"+method.getName());
		}catch(Exception e){
		e.printStackTrace();
		}
		return result;
	}

}
4.运行结果
03/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 beforefindInfo
#####你目前输入的名字是:阿飞
#####业务代码xxx1
#####业务代码xxx2
03/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 afterfindInfo

5.分析一下。这个是最基本的JDK动态代理,通过java反射原理,将需要代理的method前后绑定了逻辑,不影响其方法内的运行!
缺陷是,该方法必须是通过实现接口的。
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this)

二、cglib动态代理实现AOP拦截

还是拿刚才的代理类做示范。
一, cglib代理处理类(实现了MethodInterceptor类,用将目标类传进 Enhancer调用 intercept方法,intercept和jdk invoke类似

package com.handler.AOPInstrumenter;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import org.apache.log4j.Logger;

public class AOPInstrumenter implements MethodInterceptor {
	private Logger log = Logger.getLogger(AOPInstrumenter.class);
	private Enhancer enhancer = new Enhancer();

	public Object getInstrumentedClass(Class<?> clz) {
		enhancer.setSuperclass(clz);
		//this便是传进来的clz,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法  
		enhancer.setCallback(this);
		return enhancer.create();
	}

	public Object intercept(Object o, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 请在这里插入代码,在方法前调用
		log.debug("调用log日志方法 before" + method.getName());
		Object result = proxy.invokeSuper(o, args);
		// 请在这里插入代码,方法后调用
		log.debug("调用log日志方法 after" + method.getName());
		return result;
	}
}

二 测试代码
	AOPInstrumenter instrumenter=new AOPInstrumenter();
		StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class);
		studentInfo.findInfo("阿飞");
三 ,结果
03/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 beforefindInfo
#####你目前输入的名字是:阿飞
#####业务代码xxx1
#####业务代码xxx2
03/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 afterfindInfo

完成,基本的原来就是这些,spring 源码使用它的AOP当然远远比这个复杂,后续再使用spring aop分析



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值